package cn.tedu.charging.device.dao.impl;

import cn.tedu.charging.device.pojo.po.StationInfoWrapper;
import cn.tedu.charging.device.pojo.po.StationPO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.BoundGeoOperations;
import org.springframework.data.redis.core.RedisTemplate;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public abstract class AbstractStationCacheTemplate  {


    public List<StationInfoWrapper> search(Double longitude, Double latitude, Double distance) {
        BoundGeoOperations stations = getRedisTemplate().boundGeoOps("stations");
        //通过经纬度和距离构建一个圆
        Circle circle = getCircle(longitude, latitude, distance);
        //获取查询的参数
        RedisGeoCommands.GeoRadiusCommandArgs geoRadiusArgs = getGeoRadiusArgs();

        log.debug("通过RedisGEO Circle:{} 查询附近充电站",circle);
        GeoResults<RedisGeoCommands.GeoLocation<Integer>>
                radius = stations.radius(circle,geoRadiusArgs);
        log.debug("通过RedisGEO Circle:{} 查询附近充电站:{}",circle,radius);

        List<StationInfoWrapper> result = new ArrayList<>();

        for (GeoResult<RedisGeoCommands.GeoLocation<Integer>> geoLocationGeoResult : radius) {
            RedisGeoCommands.GeoLocation<Integer> content =
                    geoLocationGeoResult.getContent();
            //动态的距离 用户动态的位置 和 站点固定的位置 之间的距离
            Distance resultDistance = geoLocationGeoResult.getDistance();
            log.debug("当前充电站和用户位置的距离:{}", resultDistance);
            double value = resultDistance.getValue();
            //add(point,member)
            //content.getName() 就是 add的 member 基本信息
            //content.getPoint() 就是 add 的 point 位置信息
            Integer stationId = content.getName();

            StationPO stationPO
                    = getStationInfoById(String.valueOf(stationId));


            StationInfoWrapper stationInfo = new StationInfoWrapper();

            stationPO.setId(stationId);

            stationInfo.setStationPO(stationPO);
            stationInfo.setDistance(value);
            result.add(stationInfo);
        }
        return result;
    }

    public abstract StationPO getStationInfoById(String stationId);


    /**
     * 保存位置信息 到 GEO
     * 同时保存 member为 位置对应的站id
     * @param stationPOs
     */
    public void saveGEOByRedisGEOAndMemberIsId(List<StationPO> stationPOs) {
        Map<Integer, Point> stationPOMap = new HashMap<>();
        for (StationPO stationPO : stationPOs) {
            //获取经度
            BigDecimal stationLng = stationPO.getStationLng();
            //获取纬度
            BigDecimal stationLat = stationPO.getStationLat();
            //通过 经纬度 构建RedisGEO 中的点 Point
            Point point = new Point(Double.valueOf(stationLng.toEngineeringString()),
                    Double.parseDouble(stationLat.toEngineeringString()));
            stationPOMap.put(stationPO.getId(),point);
        }
        BoundGeoOperations stations = getRedisTemplate()
                .boundGeoOps("stations");
        stations.add(stationPOMap);
    }

    public abstract RedisTemplate getRedisTemplate();

    /**
     * 构建查询的参数
     */
    public RedisGeoCommands.GeoRadiusCommandArgs getGeoRadiusArgs(){
        RedisGeoCommands.GeoRadiusCommandArgs args =
                RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
        //设置参数,在查询的时候,返回距离,
        //站的位置是固定的,查询用户的位置动态的
        //用户和站的距离是动态的
        args.includeDistance();
        //设置参数 在查询的时候 返回 空间坐标(Coordinates) 经纬度,方便地图绘制
        args.includeCoordinates();
        return args;
    }

    /**
     * 通过点和半径构建一个圆
     * @param longitude
     * @param latitude
     * @param distance
     * @return
     */
    public Circle getCircle(Double longitude, Double latitude, Double distance) {
        //通过经纬度和距离 创建一个GEO 里的 圆
        //通过入参的经纬度 构建一个点
        Point point = new Point(longitude, latitude);
        //通过入参的距离 设置半径 同时设置单位为 KILOMETERS
        Distance geoDistance = new Distance(distance, Metrics.KILOMETERS);
        //通过点和半径 画圆
        Circle circle = new Circle(point,geoDistance);
        //Circle circle = new Circle(longitude,latitude,distance);
        return circle;
    }
}
